#include "preimage.h"

roPreImage::roPreImage()
{
	m_data = 0;
	m_width = 0;
	m_height = 0;
	m_halftone = 0;
}

roPreImage::roPreImage(uchar* data, uint width, uint height)
{
	replace(data, width, height);
}

roPreImage::roPreImage(roPreImage* source)
{
	replace(source->data(), source->width(), source->height());
}

roPreImage::roPreImage(uint width, uint height, bool fill, uchar colour)
{
	uchar* data = 0;
	if(fill)
	{
		data = new uchar[height * width * 3];
		for (uint y = 0; y < height; ++ y)
			for (uint x = 0; x < width; ++ x)
			{
				long xy = (y * width + x) * 3;
				for(uint i = 0; i < 3; ++ i)
				{
					long xyi = xy + i;
					data[xyi] = colour;
				}
			}
	}
	replace(data, width, height);
}

bool roPreImage::replace(uchar* data, int width, int height)
{
	if(data)
	{
		m_width = width;
		m_height = height;
		m_data = data;
		m_halftone = 0;
	}
	else
		return false;
	return true;
}

char* roPreImage::data_c()
{
	char* data = new char[m_height * m_width * 3];
	for (uint y = 0; y < m_height; ++ y)
		for (uint x = 0; x < m_width; ++ x)
		{
			long xy = y * m_width + x;
			data[xy] = (char)(m_data[xy]);
		}

		return data;
}
roPreImage* roPreImage::area(int x0, int y0, uint width, uint height)
{
	x0 = (x0 < 0? 0 : x0);
	y0 = (y0 < 0? 0 : y0);
	width = (x0 + width < m_width ? width : m_width - x0);
	height = (y0 + height < m_height ? height : m_height - y0);
	uchar* data = new uchar[height * width * 3];

	uint s = x0 * y0 * 3;

	for (uint y = y0; y < y0 + height; ++ y)
		for (uint x = x0; x < x0 + width; ++ x)
		{
			long xy = y * m_width + x;
			long xyn = (y - y0) * width + (x - x0);
			for (uint i = 0; i < 3; ++ i)
				data[xyn * 3 + i] = m_data[xy * 3 + i];
		}
		return new roPreImage(data, width, height);
}
uchar * roPreImage::data()
{
	return m_data;
}

uint roPreImage::width()
{
	return m_width;
}

uint roPreImage::height()
{
	return m_height;
}

uchar* roPreImage::pixel(uint x, uint y)
{
	return m_data + (y * m_width + x) * 3;
}

uchar* roPreImage::greyscale(bool recalc)
{
	double red2Gray   = 0.297;
	double green2Gray = 0.589;
	double blue2Gray  = 0.114;

	if(m_halftone != 0 && recalc == false) return m_halftone;
	m_halftone = new uchar[m_width * m_height];

	for (uint y = 0; y < m_height; ++ y)
		for (uint x = 0; x < m_width; ++ x)
		{
			long xy = (y * m_width + x);
			long pos = xy * 3;


			m_halftone[xy] = (uchar)(m_data[pos] * red2Gray +
				m_data[pos+1] * green2Gray +
				m_data[pos+2] * blue2Gray
				);
		}
		return m_halftone;
}

uchar* roPreImage::greyscale(uchar* halftone)
{
	long xy;
	if(m_halftone == 0)
		m_halftone = new uchar[m_width * m_height];

	for (uint y = 0; y < m_height; ++ y)
		for (uint x = 0; x < m_width; ++ x)
		{
			xy = (y * m_width + x);
			m_halftone[xy] = halftone[xy];
		}

		return m_halftone;
}

uchar* roPreImage::chanel(const uint component)
{
	uchar* chanel = new uchar[m_width * m_height];
	long pos, xy;
	for (uint y = 0; y < m_height; ++ y)
		for (uint x = 0; x < m_width; ++ x)
		{
			xy = (y * m_width + x);
			pos = xy * 3 + component;
			chanel[xy] = m_data[pos];
		}
		return chanel;
}

void roPreImage::substitute(const uint component, uchar* data)
{
	long pos, xy;
	for (uint y = 0; y < m_height; ++ y)
		for (uint x = 0; x < m_width; ++ x)
		{
			xy = (y * m_width + x);
			pos = xy * 3 + component;
			m_data[pos] = data[xy];
		}
}

roPreImage* roPreImage::clone()
{
	return area(0, 0, m_width, m_height);
}

roPreImage roPreImage::operator_rel(int type, roPreImage param)
{
	roPreImage* temp = clone();
	uchar* data = temp->data();
	uchar* data2 = param.data();

	if(type == 1)//"+"
	{
		for (uint x = 0; x < m_width; ++ x)
			for (uint y = 0; y < m_height; ++ y)
			{
				long xy = (y * m_width + x) * 3;
				for(uint i = 0; i < 3; ++ i)
				{
					long xyi = xy + i;
					if(param.width() > x && param.height() > y)
						data[xyi] = (char)(((int)data[xyi] + (int)data2[(y * param.width() + x) * 3 + i]) % 256);
				}
			}
	}
	else if(type == 2)//"-"
	{
		int t;
		for (uint y = 0; y < m_height; ++ y)
			for (uint x = 0; x < m_width; ++ x)
			{
				long xy = (y * m_width + x) * 3;
				for(uint i = 0; i < 3; ++ i)
				{
					long xyi = xy + i;
					if(param.width() > x && param.height() > y)
					{
						t = (int)data[xyi] - (int)data2[(y * param.width() + x) * 3 + i];
						data[xyi] = (char)((t < 0? 0 : t) % 256);
					}
				}
			}
	}
	return (*temp);
}

roPreImage roPreImage::operator- (roPreImage param)
{
	return operator_rel(2, param);
}

roPreImage roPreImage::operator+ (roPreImage param)
{
	return operator_rel(1, param);
}

void roPreImage::lengthen(int direct, uint value, ulong colour)
{
	uchar* data = 0;
	uint width = m_width, height = m_height;
	switch(direct)
	{
	case UP:
	case DOWN:
		height += value;
		data = new uchar[width * (height) * 3];
		for (uint y = 0; y < height; ++ y)
			for (uint x = 0; x < width; ++ x)
			{
				long xy = (y * width + x) * 3;
				for(uint i = 0; i < 3; ++ i)
				{
					if(direct == UP)
					{
						if(y < value)
							data[xy + i] = 0;
						else
							data[xy + i] = m_data[((y - value) * width + x) * 3 + i];
					}
					else
					{
						if(y >= m_height)
							data[xy + i] = 0;
						else
							data[xy + i] = m_data[xy + i];	
					}
				}
			}
		break;
	case RIGHT:
	case LEFT:
		width += value;
		data = new uchar[width * height * 3];
		for (uint y = 0; y < height; ++ y)
			for (uint x = 0; x < width; ++ x)
			{
				long xy = (y * width + x) * 3;
				for(uint i = 0; i < 3; ++ i)
				{
					if(direct == LEFT)
					{
						if(x < value)
							data[xy + i] = 0;
						else
							data[xy + i] = m_data[(y * m_width + (x - value)) * 3 + i];
					}
					else
					{
						if(x >= m_width)
							data[xy + i] = 0;
						else
							data[xy + i] = m_data[(y * m_width + x) * 3 + i];	
					}
				}
			}
		break;
	case AROUND:
		lengthen(UP, value, colour);
		lengthen(RIGHT, value, colour);
		lengthen(DOWN, value, colour);
		lengthen(LEFT, value, colour);
		return;
		break;
	}
	replace(data, width, height);
}



